import { DslRootElement, RootElement, SchemaElement } from "../server/schema-element";
import { Location } from "../protocol/token.service";
import { findNodeByLocation, findTypeElement } from "./schema-util";
import { DslAtomicType, DslStructType, DslType } from "./type-definition";

// const类型，选择的时候，自动创建输入框并且输入值，然后type标签为const string,const number这样的
interface TypeService {
    /**
     * 获取下一个输入元素的值，返回null表示下一个输入元素的值可能存在多个
     * @param location 当前元素所在的位置
     */
    inputComplete(location: Location): SchemaElement | string | null;
    
    /**
     * 获取当前位置可以输入的值列表
     * @param location 当前位置
     * @param inputValue 当前位置已经输入的值
     */
    getCandidateTokens(location: Location, inputValue: string): Array<string>;

    /**
     * 检查此位置输入的值是否合法
     * @param location 位置
     */
    check(location: Location): string | null;

    /**
     * 获取root元素
     */
    getRootElement(): RootElement;
}

/**
 * 此处的类型系统支持以下类型，后续可以新增其他类型
 *  - 结构体类型
 *  - tuple类型
 *  - 数组类型
 *  - 基础类型
 *  - 常量类型
 *  - UnionType
 * 此外：因为存在类型标记，所以输入前都已经明确了类型
 * 每个可选的类型都必须注册一个名称
 */
/**
 * TypeService对外提供的API
 * - 根据location获取当前输入框可能的值列表和类型，如果只有一个值，那么服务端创建只读元素，并且自动完成
 * - 根据location、输入值获取当前输入框可能的值列表
 * - 输入完成后，校验值是否合法，并且返回错误消息
 * - 创建类型的初始元素
 */
// rootElement和type类型匹配，所以create过程也是由类型服务代理
// 常量类型的特殊处理，
export class DslTypeService implements TypeService {
    private rootType: DslType;
    private typeNames: Map<string, DslType>;
    private rootElement: RootElement;

    constructor(rootType: DslType) {
        this.rootType = rootType;
        const typeNameVisitor = new TypeNameVisitor();
        this.rootType.accept(typeNameVisitor);
        this.typeNames = typeNameVisitor.typeNames;
        this.rootElement = new DslRootElement(this.rootType);
    }

    inputComplete(location: Location): SchemaElement {
        const node = findNodeByLocation(this.rootElement, location);
        const type = node.value.type;

        // type在createInitElement方法中处理
        if (type === 'type') {
            const typeName = (node.value as SchemaTypeElement).typeName;
            // 对常量类型特殊处理
            if (typeName.startsWith('const ')) {
                const constTypeInfo = typeName.split(' ');
                if (constTypeInfo.length !== 3) {
                    throw new Error(`Invalid const type name: ${typeName}`);
                }

                const constTypeName = constTypeInfo[1];
                const constTypeValue = constTypeInfo[2];
                const typeObject = this.typeNames.get(constTypeName);
                if (!typeObject) {
                    throw new Error(`Type name ${constTypeName} not found.`);
                }
                if (!(typeObject instanceof DslAtomicType)) {
                    throw new Error(`Type ${constTypeName} is not an atomic type.`);
                }
                const valid: boolean = typeObject.valueIsType(constTypeValue);
                if (!valid) {
                    throw new Error(`Value ${constTypeValue} is not valid for type ${constTypeName}.`);
                }
                return typeObject.createInitElement(node.value.parentElement!);
            } else {
                const typeObject = this.typeNames.get(typeName);
                if (!typeObject) {
                    throw new Error(`Type name ${typeName} not found.`);
                }
                return typeObject.createInitElement(node.value.parentElement!);
            }
        }

        if (type === 'key') {
            let typeObject: DslType;
            const parent = node.value.parentElement!;
            if (parent.type === 'root') {
                typeObject = this.rootType;
            } else if (parent.type === 'schemaObject') {
                const typeElement = findTypeElement(parent);
                const typeName = typeElement.typeName;

                const t = this.typeNames.get(typeName);
                if (!t) {
                    throw new Error(`parent element's type name ${typeName} not found.`);
                }
                typeObject = t;
            } else {
                throw new Error(`Unsupported parent type: ${parent.type}`);
            }

            if (!(typeObject instanceof DslStructType)) {
                throw new Error(`Type ${typeObject} is not an atomic type.`);
            }

            const field = typeObject.getField((node.value as SchemaKeyElement).key);
            const fieldType = field.type;
            const typeElement: SchemaTypeElement = {
                type: 'type',
                typeName: fieldType.typeName,
                readonly: (!fieldType.typeName),
                parentElement: parent
            };
            return typeElement;
        }
        if (type === 'value') {
            // value需要判断当前上下文是否结束
            const parent = node.value.parentElement!;
            if (parent.type === 'schemaObject' || parent.type === 'schemaArray') {
                const typeElement = findTypeElement(parent);
                const typeName = typeElement.typeName;

                const t = this.typeNames.get(typeName);
                if (!t) {
                    throw new Error(`parent element's type name ${typeName} not found.`);
                }
                return t.createInitElement(parent);
            }
            throw new Error(`Unsupported parent type: ${parent.type}`);
        }
        throw new Error(`Unsupported type: ${type}`);
    }
    
    getCandidateTokens(location: Location, inputValue: string): Array<string> {
        throw new Error("Method not implemented.");
    }
    
    check(location: Location): string | null {
        throw new Error("Method not implemented.");
    }

    getRootElement(): RootElement {
        return this.rootElement;
    }
}
